﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace PlutoStudio.Net.Http
{
    /// <summary>
    /// 
    /// </summary>
    public class HttpClient
    {
        private Uri _url;

        private System.Net.Http.HttpContent _content;

        private X509CertificateCollection certificates = new X509CertificateCollection();
        /// <summary>
        /// 
        /// </summary>
        public HttpStatusCode StatusCode { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public string ResponseBody { get; private set; }

        private HttpClient(string url)
        {
            this._url = new Uri(url);
        }

        private HttpClient(Uri url)
        {
            this._url = url;
        }

        private static Uri MergeUrl(Uri url, IDictionary<string, string> parameter)
        {
            if (parameter == null)
            {
                return url;
            }
            var uri = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url.AbsoluteUri, parameter);
            return new Uri(uri);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static HttpClient Create(string url)
        {
            return new HttpClient(url);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static HttpClient Create(Uri url)
        {
            return new HttpClient(url);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public HttpClient Parameter(IDictionary<string, string> parameter)
        {
            this._url = MergeUrl(this._url, parameter);
            return this;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public HttpClient Parameter(object parameter)
        {
            var para = new RouteValueDictionary(parameter);
            return this.Parameter(para);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public async Task<string> GetStringAsync()
        {
            using (var message = await GetAsync())
            {
                return await message.Content.ReadAsStringAsync();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string GetString()
        {
            using (var message = Get())
            {
                return message.Content.ReadAsStringAsync().Result;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public async Task<System.IO.Stream> GetStreamAsync()
        {
            using (var message = await GetAsync())
            {
                return await message.Content.ReadAsStreamAsync();
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public System.IO.Stream GetStream()
        {
            using (var message = Get())
            {
                return message.Content.ReadAsStreamAsync().Result;
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public async Task<System.Net.Http.HttpResponseMessage> GetAsync()
        {
            using (var client = GetClient())
            {
                var result = await client.GetAsync(this._url);
                StatusCode = result.StatusCode;
                return result;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public System.Net.Http.HttpResponseMessage Get()
        {
            using (var client = GetClient())
            {
                var result = client.GetAsync(this._url).Result;
                StatusCode = result.StatusCode;
                return result;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async Task<T> GetAsync<T>()
        {
            return (await GetStringAsync()).Deserialize<T>();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Get<T>()
        {
            return (GetString()).Deserialize<T>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public async Task<System.Net.Http.HttpResponseMessage> PostAsync()
        {
            using (var client = GetClient())
            {
                var result = await client.PostAsync(this._url, _content);
                StatusCode = result.StatusCode;
                return result;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public System.Net.Http.HttpResponseMessage Post()
        {
            using (var client = GetClient())
            {
                var result = client.PostAsync(this._url, _content).Result;
                StatusCode = result.StatusCode;
                return result;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public async Task<System.IO.Stream> PostStreamAsync()
        {
            using (var message = await PostAsync())
            {
                return await message.Content.ReadAsStreamAsync();
            }

        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public System.IO.Stream PostStream()
        {
            using (var message = Post())
            {
                return message.Content.ReadAsStreamAsync().Result;
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public async Task<string> PostStringAsync()
        {
            using (var message = await PostAsync())
            {
                ResponseBody = await message.Content.ReadAsStringAsync();
                return ResponseBody;
            }

        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string PostString()
        {
            using (var message = Post())
            {
                ResponseBody = message.Content.ReadAsStringAsync().Result;
                return ResponseBody;
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async Task<T> PostAsync<T>()
        {
            var value = await PostStringAsync();
            return value.Deserialize<T>();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Post<T>()
        {
            var value = PostString();
            return value.Deserialize<T>();
        }

        private System.Net.Http.HttpClient GetClient()
        {
            var handler = new System.Net.Http.HttpClientHandler();
            if (certificates.Count > 0)
            {
                handler.ClientCertificateOptions = System.Net.Http.ClientCertificateOption.Manual;
                handler.ClientCertificates.AddRange(certificates);
            }
            else
            {
                handler.ClientCertificateOptions = System.Net.Http.ClientCertificateOption.Automatic;
            }
            return new System.Net.Http.HttpClient(handler);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="content"></param>
        /// <param name="contentType"></param>
        /// <returns></returns>
        public HttpClient Content(string content, string contentType = "application/json")
        {
            this._content = new System.Net.Http.StringContent(content, Encoding.UTF8, contentType);
            return this;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="content"></param>
        /// <param name="contentType"></param>
        /// <returns></returns>
        public HttpClient Content(string content, Encoding encoding, string contentType = "application/json")
        {
            this._content = new System.Net.Http.StringContent(content, encoding, contentType);
            return this;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public HttpClient Content(System.IO.Stream content)
        {
            this._content = new System.Net.Http.StreamContent(content);
            return this;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public HttpClient Content(System.IO.Stream content, string name)
        {
            var mfdc = new System.Net.Http.MultipartFormDataContent();
            mfdc.Add(new System.Net.Http.StreamContent(content), name);
            this._content = mfdc;
            return this;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="content"></param>
        /// <param name="contentType"></param>
        /// <returns></returns>
        public HttpClient Content(object content, ContentType contentType)
        {
            return Content(new RouteValueDictionary(content), contentType);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="content"></param>
        /// <param name="contentType"></param>
        /// <returns></returns>
        public HttpClient Content(IDictionary<string, object> content, ContentType contentType)
        {
            switch (contentType)
            {
                case ContentType.FormData:
                    _content = new System.Net.Http.FormUrlEncodedContent(content.Select(p => new KeyValuePair<string, string>(p.Key, p.Value?.ToString())));
                    break;
                case ContentType.Json:
                    _content = new System.Net.Http.StringContent(content.Serialize(), Encoding.UTF8, "application/json");
                    break;
            }

            return this;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="certificate"></param>
        /// <returns></returns>
        public HttpClient Certificate(X509Certificate certificate)
        {
            certificates.Add(certificate);
            return this;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public enum ContentType
    {
        /// <summary>
        /// 
        /// </summary>
        FormData,
        /// <summary>
        /// 
        /// </summary>
        Json
    }
}
